﻿//
//  Monkey Manager
//  Copyright (C) 2012 Victor Tereschenko
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//     
// ========================================================================

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading;

using ManagerDB;

namespace AddManager
{
    /// <summary>
    /// Arguments for file adding event.
    /// </summary>
    class FileAddingEventArgs : EventArgs
    {
        public FileAddingEventArgs(FileHashInfo file)
        {
            File = file;
        }

        public FileHashInfo File { get; private set; }
    }

    /// <summary>
    /// Agruments for file added event.
    /// </summary>
    class FileAddedEventArgs : EventArgs
    {
        public FileAddedEventArgs(FileHashInfo file)
        {
            File = file;
        }

        public FileHashInfo File { get; private set; }
    }

    /// <summary>
    /// Agruments for file add error event.
    /// </summary>
    class FileAddErrorEventArgs : EventArgs
    {
        public FileAddErrorEventArgs(FileHashInfo file, string error)
        {
            File = file;
            Error = error;
        }

        public FileHashInfo File { get; private set; }
        public string Error { get; private set; }
    }

    /// <summary>
    /// Adds files into DB.
    /// </summary>
    class AddController : IDisposable
    {
        IMutableDatabase mDatabase;
        public AddController(IMutableDatabase database)
        {
            mDatabase = database;
            mEvent = new ManualResetEvent(false);
            mThread = new Thread(addFiles);
            mThread.Start();
        }

        Thread mThread;
        ManualResetEvent mEvent;
        ConcurrentQueue<Tuple<FileHashInfo, bool>> mFilesToAdd = new ConcurrentQueue<Tuple<FileHashInfo, bool>>();

        public EventHandler<FileAddingEventArgs> FileAdding;
        public EventHandler<FileAddedEventArgs> FileAdded;
        public EventHandler<FileAddErrorEventArgs> FileAddError;

        public void Add(FileHashInfo file, bool move)
        {
            var item = new Tuple<FileHashInfo, bool>(file, move);
            mFilesToAdd.Enqueue(item);
            mEvent.Set();
        }

        void addFiles()
        {
            while (true)
            {
                Tuple<FileHashInfo, bool> file;
                while (mFilesToAdd.TryDequeue(out file))
                {
                    FileAdding(this, new FileAddingEventArgs(file.Item1));
                    var item = mDatabase.Find(file.Item1.TTH, file.Item1.FileInfo.Length);
                    if (item != null)
                    {
                        var args = new FileAddErrorEventArgs(file.Item1, string.Format("Item already exists in DB: {0}", item.File.FileName));
                        FileAddError(this, args);
                    }
                    else
                    {
                        try
                        {
                            mDatabase.Add(file.Item1, file.Item2);
                            FileAdded(this, new FileAddedEventArgs(file.Item1));
                        }
                        catch (Exception ex)
                        {
                            FileAddError(this, new FileAddErrorEventArgs(file.Item1, ex.Message));
                        }
                    }
                }
                mEvent.WaitOne();
                mEvent.Reset();
            }
        }

        bool mDisposing = false;
        public void Dispose()
        {
            if (mDisposing)
                return;
            mDisposing = true;

            mThread.Abort();
        }
    }
}
